﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Concurrent;
using System.Reflection;
using Framework.Extensions;

namespace Framework.Reflection
{
    public static class FastObjectAccessor
    {
        public static void CloneMembers(object from, object to)
        {
            CloneMembers(from, to, null);
        }
        public static void CloneMembers(object from, object to, IEnumerable<string> excludedProperties)
        {
            var SourceProperties = DynamicsCache.GetAccessors(from.GetType()); // typeof(T).GetPublicProperties().Where(x => x.CanWrite);
            var DestinationProperties = DynamicsCache.GetAccessors(to.GetType()); // typeof(T).GetPublicProperties().Where(x => x.CanWrite);

            foreach (var prop in DestinationProperties)
            {
                if (excludedProperties == null || !excludedProperties.Contains(prop.PropertyName))
                {
                    var sourceProperty = SourceProperties.FirstOrDefault(x => x.PropertyName == prop.PropertyName);

                    if (sourceProperty != null)
                    {
                        object val = sourceProperty.Getter.Invoke(from, null);
                        prop.Setter.Invoke(to, val);
                    }
                }
            }

        }

        public static T To<T>(object o) where T : new()
        {
            if (o == null) return default(T);


            var SourceProperties = DynamicsCache.GetAccessors(o.GetType()); // typeof(T).GetPublicProperties().Where(x => x.CanWrite);
            var DestinationProperties = DynamicsCache.GetAccessors<T>(); // typeof(T).GetPublicProperties().Where(x => x.CanWrite);

            var newObject = new T();

            foreach (var prop in DestinationProperties)
            {
                var sourceProperty = SourceProperties.FirstOrDefault(x => x.PropertyName == prop.PropertyName);

                if (sourceProperty != null)
                {
                    object val = sourceProperty.Getter.Invoke(o, null);
                    prop.Setter.Invoke(newObject, val);
                }
            }

            return newObject;
        }

        public static T DynamicTo<T>(dynamic o) where T : new()
        {
            var dic = o as IDictionary<string, object>;
            if (dic == null) return default(T);
            var props = DynamicsCache.GetAccessors<T>();

            var newObject = new T();

            foreach (var prop in props)
            {
                object val;
                if (dic.TryGetValue(prop.PropertyName, out val))
                {
                    prop.Setter.Invoke(newObject, val);
                }
            }

            return newObject;
        }

    }
}
